"""
  Copyright (c) 2024 Northeastern University (China)
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
"""
# 任务：构建各个模块之间的依赖关系，通过上述已经得到的各个模块的特征和各个模块依赖语句对比来构建
# 输入：dependency_dic，target_features
# 输出：final_dependency=[
#    {
#    'name': 模块名,
#    'final_dep': [{'oss':依赖的模块特征(是target_feature中的一个元素),
#                   'dependncy':{statement':依赖语句,
#                                 'filepath':依赖语句所在的文件路径} }]
#     'final_depname':该模块依赖的所有模块名称
#    }
# ]

import json
import networkx as nx
import matplotlib.pyplot as plt

from src.common_utils.utils_file import read_jl
from src.dependency_construction.indirect_dependency import checking_Identifier_in_source, checking_Identifier_in_source_main


class BuildDependency:

    def draw_graph(self,graph):
        # 可视化图
        # 绘制图形
        # 计算节点布局
        pos = nx.spring_layout(graph, k=1)  # 增加 k 的值来增加节点间距
        # 设置绘图窗口的大小
        plt.figure(figsize=(10, 10))
        node_labels = {node: str(node)[:12] for node in graph.nodes()}
        # 设置节点颜色
        node_colors = ['lightgreen' if node == 'application' else 'lightcoral'
                       for node in graph.nodes()]
        # 绘制图形
        nx.draw(graph, pos, with_labels=False, node_color=node_colors, node_size=1000, font_size=12, edge_color='black',
                width=1.0, arrows=True, arrowstyle='->')
        # 添加节点标签并调整位置
        nx.draw_networkx_labels(graph, pos, labels=node_labels, font_size=8, verticalalignment='top')
        # 显示图形
        plt.title("Graph Visualization")
        plt.show()

    def build_dependency(self,dependency_dic,target_features,target_path,numprocess=8):
        """
        将每个语句的依赖语句信息和之前已经存储下的每个模块的特征做对比
        1.#include特征按照路径节点对比
        2.extern特征按照类型，签名，标识符名称对比
        3.增加间接依赖识别
        @param dependency_dic:
        @param target_features:
        @return:
        """

        def split_path(path):
            # 使用split函数按照路径分隔符\切分路径字符串，并去掉空节点
            return [node for node in path.split('\\') if node]

        def is_short_path_contained_in_long_path(short_path, long_path):
            # 分割段路径和长路径
            short_path_nodes = split_path(short_path)
            long_path_nodes = split_path(long_path)

            # 从最后一个节点开始比较
            short_index = len(short_path_nodes) - 1
            long_index = len(long_path_nodes) - 1

            while short_index >= 0 and long_index >= 0:
                if short_path_nodes[short_index] != long_path_nodes[long_index]:
                    # 如果节点不匹配，返回False
                    return False
                short_index -= 1
                long_index -= 1

            # 如果段路径的所有节点都按顺序包含在长路径中，则返回True，否则返回False
            return short_index < 0
        final_dependency = []
        for moudle_dep in dependency_dic:#逐个处理每个模块的依赖信息
            oss_name = moudle_dep['name']
            include_dependency_info = moudle_dep['include_dependency']
            extern_dependency_info = moudle_dep['extern_dependency']
            dep_info = {
                'source': oss_name
            }
            for moudle in target_features:  # 每个依赖信息都要与所有模块特征做对比
                dep_signal = False
                moudle_name = moudle['name']
                if moudle_name == oss_name:  # 如果被比对的模块是自己本身则跳过
                    continue
                else:
                    print(f"{oss_name}->{moudle_name}开始include检测")
                    for dep in include_dependency_info:#逐个处理该模块的依赖信息
                        dep_statement=dep['statement']
                        moudle_feature = moudle['head_file_feature']
                        for future in moudle_feature:#每条依赖信息与模块所有特征做对比
                            if is_short_path_contained_in_long_path(dep_statement,future)is True:#说明#include的相对路径被包含在绝对路径中
                                dep_info['dependency_type']='#include'
                                dep_info['dependency_statement']=dep
                                dep_info['dependency_file']=future
                                dep_info['target']=moudle_name
                                dep_signal = True
                                final_dependency.append(dep_info)
                                break
                        if dep_signal:
                            break
                            # 把一个include的头文件全部检测出
                    if not dep_signal:
                        print(f"{oss_name}->{moudle_name}开始extern检测")
                        for dep in extern_dependency_info:  # 逐个处理该模块的依赖信息
                            if len(dep)==0:
                                continue
                            moudle_feature = moudle['global_feature']
                            if len(moudle_feature)==0:
                                continue
                            for future in moudle_feature:  # 每条依赖信息与模块所有特征做对比
                                if dep['statement_type'] == future['feature_type']:
                                    if dep['name']==future['name']:
                                        if dep['type']==future['type']:
                                            if 'signature' in dep :
                                                if  dep['signature'] == future['signature']:
                                                    dep_info['dependency_type']='extern function'
                                                    dep_info['dependency_statement']=dep
                                                    dep_info['dependency_file']=future['filepath']
                                                    dep_info['target'] = moudle_name
                                                    final_dependency.append(dep_info)
                                                    dep_signal = True
                                                    break
                                            else:
                                                dep_info['dependency_type'] = 'extern var'
                                                dep_info['dependency_statement']=dep
                                                dep_info['dependency_file']=future['filepath']
                                                dep_info['target'] = moudle_name
                                                final_dependency.append(dep_info)
                                                dep_signal = True
                                                break
                            if dep_signal:
                                break
                    if not dep_signal:
                        indirect_feature = moudle['indirect_feature']
                        if len(indirect_feature)!=0:
                            print(f"{oss_name}->{moudle_name}开始indirect检测")
                            source_filepaths=[]
                            for oss in target_features:
                                if oss['name']==oss_name:
                                    source_filepaths = oss['head_file_feature']  # 表示这个模块所有的文件
                                    break
                            final_dep_info, dependency_type, appear = checking_Identifier_in_source_main(filepaths=source_filepaths,
                                                                                                Identifier=indirect_feature,target_path=target_path,numprocess=numprocess)
                            if len(final_dep_info)!=0:
                                for data in final_dep_info:
                                    dep_info['dependency_type']=data['dependency_type']
                                    dep_info['dependency_statement']=data['dependency']
                                    dep_info['dependency_id']=data['dependency_id']
                                    dep_info['target']=moudle_name
                                    final_dependency.append(dep_info)
        return final_dependency

if __name__ == "__main__":
    dependency_statement_path=input("dependency_statement_path:")
    dependency_dic=read_jl(dependency_statement_path)
    targetpath=input("target:")
    feature_dict_path=input("feature_dict_path:")
    target_features =read_jl(feature_dict_path)
    buildDependency=BuildDependency()
    final_dependency=buildDependency.build_dependency(dependency_dic,target_features,target_path=targetpath)
    fres = open("result_build_dependency.json", 'w')
    for dic in final_dependency:
        json_str = json.dumps(dic)
        fres.write(json_str + '\n')


